#include <string>
#include <iostream>
#include <mutex>
#include <fstream>
#include <filesystem>
#include <memory>
#include <ctime>
#include <sstream>
#include <unistd.h>
#include <sys/types.h>


namespace log_module
{
    const std::string default_path = "./";
    const std::string default_file_name = "log.txt";

    enum class LOG_LEVEL
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };



    std::string log_level_to_string(LOG_LEVEL level)
    {
        switch (level)
        {
        case LOG_LEVEL::DEBUG:
            return "DEBUG";
        case LOG_LEVEL::INFO:
            return "INFO";
        case LOG_LEVEL::WARNING:
            return "WARNING";
        case LOG_LEVEL::ERROR:
            return "ERROR";
        case LOG_LEVEL::FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
        return "UNKNOWN";
    }



    //检查路径是否存在，不存在则创建
    void check_or_create_path(const std::string& path)
    {
        if (!std::filesystem::exists(path))
        {
            try   
            {
                std::filesystem::create_directories(path);
            }
            catch (const std::exception& e)
            {
                std::cerr << e.what() << std::endl;
            }
        }
    }



    std::string now_time()
    {
        time_t raw_time = 0;
        time(&raw_time);
        tm* now = localtime(&raw_time);
        std::stringstream s_stream;
        s_stream << now->tm_year + 1900 << '-'
                 << now->tm_mon + 1 << '-'
                 << now->tm_mday << ' '
                 << std::setfill('0') << std::setw(2) << now->tm_hour << ':'
                 << std::setfill('0') << std::setw(2) << now->tm_min << ':'
                 << std::setfill('0') << std::setw(2) << now->tm_sec;
        return s_stream.str();
    }



    //接口类，定义了同步日志的方式
    class sync_log
    {
    public:
        virtual ~sync_log() = default;
        virtual void sync_way(const std::string& info) = 0;
    };



    //向显示器写入
    class console_log : public sync_log
    {
    public:
        virtual void sync_way(const std::string& info) override
        {
            std::lock_guard<std::mutex> lock(_mutex);
            std::cout << info << std::endl;
        }
    private:
        std::mutex _mutex;
    };



    //向文本文件写入
    class file_log : public sync_log
    {
    public:
        file_log(const std::string& path = default_path
               , const std::string& file_name = default_file_name)
            : _path(path)
            , _file_name(file_name)
        {}


        virtual void sync_way(const std::string& info) override
        {
            std::lock_guard<std::mutex> lock(_mutex);
            //检查路径是否存在，不存在则创建
            check_or_create_path(_path);
            //打开文件,追加模式
            std::fstream file(_path + _file_name, std::ios::app);   
            file << info << std::endl;
            file.close();
        }

    private:
        std::mutex _mutex;      
        std::string _path;
        std::string _file_name;
    };



    class log
    {
    public:
        log()
            : _sync_way(std::make_unique<console_log>())
        {}
        
        
        void set_console_log()
        {
            _sync_way = std::make_unique<console_log>();
        }


        void set_file_log(const std::string& path = default_path
                   , const std::string& file_name = default_file_name)
        {
            _sync_way = std::make_unique<file_log>(path, file_name);
        }


        //日志消息类, 包含了一些必要的信息
        class log_message
        {
        public:
            log_message(LOG_LEVEL level, log& l)
                : _time(now_time())
                , _level(level)
                , _pid(getpid())
                , _file_name(__FILE__)
                , _line(__LINE__)
                , _log(l)
            {
                std::stringstream s_stream;
                s_stream << "[" << _time << "] "
                        << "[" << log_level_to_string(_level) << "] "
                        << "[" << _pid << "] "
                        << "[" << _file_name << "] "
                        << "[" << _line << "] - ";
                _message = s_stream.str();
            }


            //重载<<运算符，支持多种数据类型
            template <class T>
            log_message& operator<<(const T& message)
            {
                std::stringstream s_stream;
                s_stream << _message << message << ' ';
                _message = s_stream.str();
                return *this;
            }


            //析构函数，将信息传递给同步日志的方式
            ~log_message()
            {
                _message += '\n';
                _log._sync_way->sync_way(_message);
            }


        private:
            std::string _time;
            LOG_LEVEL _level;
            int _pid;
            std::string _file_name;
            int _line;
            std::string _message;
            log& _log;
        };

        

        log_message operator()(LOG_LEVEL level)
        {
            return log_message(level, *this);
        }
 
    private:
        std::unique_ptr<sync_log> _sync_way;
    };


    //全局日志对象
    log logger;
    inline log::log_message LOG(LOG_LEVEL level)
    {
        return logger(level);
    }


    //设置日志方式
    inline void SET_CONSOLE_LOG()
    {
        logger.set_console_log();
    }

    //设置日志方式
    void SET_FILE_LOG(const std::string& path = default_path
                   , const std::string& file_name = default_file_name)
    {
        logger.set_file_log(path, file_name);
    }
}